# Fichier : scientific_calculator/nodes.py

import math

# ==========================================================
# CATEGORIE GLOBALE POUR TOUS LES NŒUDS
# ==========================================================
MAIN_CATEGORY = "CalculatorPro"


# ===============================================
# CLASSE 1 : CALCULATRICE SCIENTIFIQUE (CORRIGÉE)
# ===============================================
class ScientificCalculatorTriNode:
    FUNCTION_CHOICES=["a+b","a-b","a*b","a/b","a^b","a mod b","a+c","a-c","a*c","a/c","a^c","a mod c","b+c","b-c","b*c","b/c","b^c","b mod c","min(a,b)","max(a,b)","min(a,c)","max(a,c)","min(b,c)","max(b,c)","a+b+c","a*b*c","sin(a)","cos(a)","tan(a)","sqrt(a)","log(a)","abs(a)","round(a)","sin(b)","cos(b)","tan(b)","sqrt(b)","log(b)","abs(b)","round(b)","sin(c)","cos(c)","tan(c)","sqrt(c)","log(c)","abs(c)","round(c)",]
    CATEGORY = f"{MAIN_CATEGORY}/Math"
    @classmethod
    def INPUT_TYPES(cls): return {"required":{"a":("FLOAT",{"default":0.0,"step":0.01}),"b":("FLOAT",{"default":0.0,"step":0.01}),"c":("FLOAT",{"default":0.0,"step":0.01}),"function":(cls.FUNCTION_CHOICES,{"default":"a+b"}),}}
    RETURN_TYPES,RETURN_NAMES,FUNCTION=("FLOAT","STRING"),("result","output_str"),"calculate"
    
    # --- MÉTHODE CORRIGÉE ---
    def calculate(self, a, b, c, function):
        res, txt, fmt = 0.0, "", "g"
        fa, fb, fc = f"{a:{fmt}}", f"{b:{fmt}}", f"{c:{fmt}}"
        
        try:
            if function == "a+b": res, txt = a + b, f"{fa} + {fb} = {a+b:{fmt}}"
            elif function == "a-b": res, txt = a - b, f"{fa} - {fb} = {a-b:{fmt}}"
            elif function == "a*b": res, txt = a * b, f"{fa} × {fb} = {a*b:{fmt}}"
            elif function == "a/b": res, txt = (a / b, f"{fa} ÷ {fb} = {a/b:{fmt}}") if b != 0 else (float('nan'), "Err:Div/0")
            elif function == "a^b": res, txt = a ** b, f"{fa} ^ {fb} = {a**b:{fmt}}"
            elif function == "a mod b": res, txt = (a % b, f"{fa} mod {fb} = {a%b:{fmt}}") if b != 0 else (float('nan'), "Err:Mod/0")
            elif function == "a+c": res, txt = a + c, f"{fa} + {fc} = {a+c:{fmt}}"
            elif function == "a-c": res, txt = a - c, f"{fa} - {fc} = {a-c:{fmt}}"
            elif function == "a*c": res, txt = a * c, f"{fa} × {fc} = {a*c:{fmt}}"
            elif function == "a/c": res, txt = (a / c, f"{fa} ÷ {fc} = {a/c:{fmt}}") if c != 0 else (float('nan'), "Err:Div/0")
            elif function == "a^c": res, txt = a ** c, f"{fa} ^ {fc} = {a**c:{fmt}}"
            elif function == "a mod c": res, txt = (a % c, f"{fa} mod {fc} = {a%c:{fmt}}") if c != 0 else (float('nan'), "Err:Mod/0")
            elif function == "b+c": res, txt = b + c, f"{fb} + {fc} = {b+c:{fmt}}"
            elif function == "b-c": res, txt = b - c, f"{fb} - {fc} = {b-c:{fmt}}"
            elif function == "b*c": res, txt = b * c, f"{fb} × {fc} = {b*c:{fmt}}"
            elif function == "b/c": res, txt = (b / c, f"{fb} ÷ {fc} = {b/c:{fmt}}") if c != 0 else (float('nan'), "Err:Div/0")
            elif function == "b^c": res, txt = b ** c, f"{fb} ^ {fc} = {b**c:{fmt}}"
            elif function == "b mod c": res, txt = (b % c, f"{fb} mod {fc} = {b%c:{fmt}}") if c != 0 else (float('nan'), "Err:Mod/0")
            elif function == "min(a,b)": res, txt = min(a,b), f"min({fa},{fb})={min(a,b):{fmt}}"
            elif function == "max(a,b)": res, txt = max(a,b), f"max({fa},{fb})={max(a,b):{fmt}}"
            elif function == "min(a,c)": res, txt = min(a,c), f"min({fa},{fc})={min(a,c):{fmt}}"
            elif function == "max(a,c)": res, txt = max(a,c), f"max({fa},{fc})={max(a,c):{fmt}}"
            elif function == "min(b,c)": res, txt = min(b,c), f"min({fb},{fc})={min(b,c):{fmt}}"
            elif function == "max(b,c)": res, txt = max(b,c), f"max({fb},{fc})={max(b,c):{fmt}}"
            elif function == "a+b+c": res, txt = a + b + c, f"{fa}+{fb}+{fc}={a+b+c:{fmt}}"
            elif function == "a*b*c": res, txt = a * b * c, f"{fa}×{fb}×{fc}={a*b*c:{fmt}}"
            elif function == "sin(a)": res, txt = math.sin(a), f"sin({fa})={math.sin(a):{fmt}}"
            elif function == "cos(a)": res, txt = math.cos(a), f"cos({fa})={math.cos(a):{fmt}}"
            elif function == "tan(a)": res, txt = math.tan(a), f"tan({fa})={math.tan(a):{fmt}}"
            elif function == "sqrt(a)": res, txt = (math.sqrt(a), f"sqrt({fa})={math.sqrt(a):{fmt}}") if a >= 0 else (float('nan'), "Err:sqrt<0")
            elif function == "log(a)": res, txt = (math.log(a), f"log({fa})={math.log(a):{fmt}}") if a > 0 else (float('nan'), "Err:log<=0")
            elif function == "abs(a)": res, txt = abs(a), f"abs({fa})={abs(a):{fmt}}"
            elif function == "round(a)": res, txt = round(a), f"round({fa})={round(a):{fmt}}"
            elif function == "sin(b)": res, txt = math.sin(b), f"sin({fb})={math.sin(b):{fmt}}"
            elif function == "cos(b)": res, txt = math.cos(b), f"cos({fb})={math.cos(b):{fmt}}"
            elif function == "tan(b)": res, txt = math.tan(b), f"tan({fb})={math.tan(b):{fmt}}"
            elif function == "sqrt(b)": res, txt = (math.sqrt(b), f"sqrt({fb})={math.sqrt(b):{fmt}}") if b >= 0 else (float('nan'), "Err:sqrt<0")
            elif function == "log(b)": res, txt = (math.log(b), f"log({fb})={math.log(b):{fmt}}") if b > 0 else (float('nan'), "Err:log<=0")
            elif function == "abs(b)": res, txt = abs(b), f"abs({fb})={abs(b):{fmt}}"
            elif function == "round(b)": res, txt = round(b), f"round({fb})={round(b):{fmt}}"
            elif function == "sin(c)": res, txt = math.sin(c), f"sin({fc})={math.sin(c):{fmt}}"
            elif function == "cos(c)": res, txt = math.cos(c), f"cos({fc})={math.cos(c):{fmt}}"
            elif function == "tan(c)": res, txt = math.tan(c), f"tan({fc})={math.tan(c):{fmt}}"
            elif function == "sqrt(c)": res, txt = (math.sqrt(c), f"sqrt({fc})={math.sqrt(c):{fmt}}") if c >= 0 else (float('nan'), "Err:sqrt<0")
            elif function == "log(c)": res, txt = (math.log(c), f"log({fc})={math.log(c):{fmt}}") if c > 0 else (float('nan'), "Err:log<=0")
            elif function == "abs(c)": res, txt = abs(c), f"abs({fc})={abs(c):{fmt}}"
            elif function == "round(c)": res, txt = round(c), f"round({fc})={round(c):{fmt}}"
            else: res, txt = float('nan'), f"Fonction non reconnue: {function}"
        except Exception as e:
            res, txt = float('nan'), f"Erreur: {e}"
        return (float(res), str(txt))

# ==========================================================
# SECTION 2 : CONVERTISSEURS
# ==========================================================
class UniversalConverterNode:
    CATEGORY = f"{MAIN_CATEGORY}/Converters"
    @classmethod
    def INPUT_TYPES(cls): return {"optional":{"float_in":("FLOAT",{"default":0.0,"forceInput":True}),"int_in":("INT",{"default":0,"forceInput":True}),"string_in":("STRING",{"default":"","forceInput":True}),"bool_in":("BOOLEAN",{"default":False,"forceInput":True}),}}
    RETURN_TYPES,RETURN_NAMES,FUNCTION=("FLOAT","INT","STRING","BOOLEAN"),("FLOAT","INT","STRING","BOOLEAN"),"convert"
    def convert(self,float_in=None,int_in=None,string_in=None,bool_in=None):
        out_float,out_int,out_string,out_bool=0.0,0,"",False
        if float_in is not None:out_float,out_int,out_string,out_bool=float(float_in),int(float_in),str(float_in),bool(float_in)
        elif int_in is not None:out_float,out_int,out_string,out_bool=float(int_in),int(int_in),str(int_in),bool(int_in)
        elif string_in is not None:
            out_string=str(string_in)
            try:out_float,out_int=float(string_in),int(float(string_in))
            except(ValueError,TypeError):out_float,out_int=0.0,0
            out_bool=string_in.lower()in['true','1','t','y','yes','vrai','o','oui']
        elif bool_in is not None:
            out_bool,out_float,out_int,out_string=bool(bool_in),1.0 if bool_in else 0.0,1 if bool_in else 0,str(bool_in)
        return(out_float,out_int,out_string,out_bool)

class ManualRateConverterNode:
    CATEGORY = f"{MAIN_CATEGORY}/Converters"
    RETURN_TYPES, RETURN_NAMES, FUNCTION = ("FLOAT", "STRING"), ("result", "output_str"), "calculate"
    OPERATION_CHOICES = ["Multiplier par le taux", "Diviser par le taux"]
    @classmethod
    def INPUT_TYPES(cls): return {"required": {"amount": ("FLOAT", {"default": 100.0}), "manual_rate": ("FLOAT", {"default": 1.07, "step": 0.0001}), "operation": (cls.OPERATION_CHOICES,)}}
    def calculate(self, amount, manual_rate, operation):
        if operation == self.OPERATION_CHOICES[0]: result = amount * manual_rate; txt = f"{amount:.2f} × {manual_rate} = {result:.2f}"
        else:
            if manual_rate == 0: return (float('nan'), "Erreur: Taux ne peut pas être zéro.")
            result = amount / manual_rate; txt = f"{amount:.2f} ÷ {manual_rate} = {result:.2f}"
        return (result, txt)

class LengthConverterNode:
    CATEGORY = f"{MAIN_CATEGORY}/Converters"
    UNITS = {"Mètre (m)": 1.0, "Centimètre (cm)": 0.01, "Millimètre (mm)": 0.001, "Kilomètre (km)": 1000.0, "Décimètre (dm)": 0.1, "Micromètre (µm)": 1e-6, "Nanomètre (nm)": 1e-9, "Angstrom (Å)": 1e-10, "Picomètre (pm)": 1e-12, "Femtomètre (fm)": 1e-15, "Pouce (in)": 0.0254, "Pied (ft)": 0.3048, "Yard (yd)": 0.9144, "Mile (mi)": 1609.344, "Mille marin (NM)": 1852.0, "Année-lumière (al)": 9.461e15, "Parsec (pc)": 3.086e16, "Unité Astronomique (ua)": 1.496e11, "Rayon terrestre (RT)": 6.371e6, "Longueur de Planck (lₚ)": 1.616e-35, "Lieue (4km)": 4000.0, "Toise (1.95m)": 1.949, "Aune (1.2m)": 1.188}
    RETURN_TYPES, RETURN_NAMES, FUNCTION = ("FLOAT", "STRING"), ("result", "output_str"), "convert"
    @classmethod
    def INPUT_TYPES(cls): return {"required": {"value": ("FLOAT", {"default": 1.0}), "from_unit": (list(cls.UNITS.keys()),), "to_unit": (list(cls.UNITS.keys()),)}}
    def convert(self, value, from_unit, to_unit):
        result = (value * self.UNITS[from_unit]) / self.UNITS[to_unit]
        return (result, f"{value:g} {from_unit.split(' (')[0]} = {result:g} {to_unit.split(' (')[0]}")

class MassConverterNode:
    CATEGORY = f"{MAIN_CATEGORY}/Converters"
    UNITS = {"Gramme (g)": 1.0, "Kilogramme (kg)": 1000.0, "Milligramme (mg)": 0.001, "Microgramme (µg)": 1e-6, "Tonne (t)": 1_000_000.0, "Livre (lb)": 453.592, "Once (oz)": 28.3495, "Grain (gr)": 0.0648, "Stone (st)": 6350.29, "Tonne UK (long ton)": 1_016_050.0, "Tonne US (short ton)": 907_185.0}
    RETURN_TYPES, RETURN_NAMES, FUNCTION = ("FLOAT", "STRING"), ("result", "output_str"), "convert"
    @classmethod
    def INPUT_TYPES(cls): return {"required": {"value": ("FLOAT", {"default": 1.0}), "from_unit": (list(cls.UNITS.keys()),), "to_unit": (list(cls.UNITS.keys()),)}}
    def convert(self, value, from_unit, to_unit):
        result = (value * self.UNITS[from_unit]) / self.UNITS[to_unit]
        return (result, f"{value:g} {from_unit.split(' (')[0]} = {result:g} {to_unit.split(' (')[0]}")

class VolumeConverterNode:
    CATEGORY = f"{MAIN_CATEGORY}/Converters"
    UNITS = {"Litre (L)": 1.0, "Millilitre (ml/cc)": 0.001, "Mètre cube (m³)": 1000.0, "Centilitre (cL)": 0.01, "Décilitre (dL)": 0.1, "Hectolitre (hL)": 100.0, "Gallon US (gal)": 3.78541, "Gallon Impérial (gal)": 4.54609, "Quart US (qt)": 0.946353, "Pinte US (pt)": 0.473176, "Pinte Impériale (pt)": 0.568261, "Cup US": 0.236588, "Fluid Ounce US (fl oz)": 0.0295735, "Fluid Ounce Impérial (fl oz)": 0.0284131, "Cuillère à soupe (15ml)": 0.015, "Cuillère à café (5ml)": 0.005, "Baril de pétrole (bbl)": 158.987, "Stère (bois)": 1000.0, "Pied cube (ft³)": 28.3168, "Pouce cube (in³)": 0.0163871}
    RETURN_TYPES, RETURN_NAMES, FUNCTION = ("FLOAT", "STRING"), ("result", "output_str"), "convert"
    @classmethod
    def INPUT_TYPES(cls): return {"required": {"value": ("FLOAT", {"default": 1.0}), "from_unit": (list(cls.UNITS.keys()),), "to_unit": (list(cls.UNITS.keys()),)}}
    def convert(self, value, from_unit, to_unit):
        result = (value * self.UNITS[from_unit]) / self.UNITS[to_unit]
        return (result, f"{value:g} {from_unit.split(' (')[0]} = {result:g} {to_unit.split(' (')[0]}")

class DataConverterNode:
    CATEGORY = f"{MAIN_CATEGORY}/Converters"
    UNITS = {"Octet (Byte)": 1.0, "Bit (b)": 1/8, "Kilo-octet (KB, 10³ B)": 1000.0, "Méga-octet (MB, 10⁶ B)": 1e6, "Giga-octet (GB, 10⁹ B)": 1e9, "Téra-octet (TB, 10¹² B)": 1e12, "Péta-octet (PB, 10¹⁵ B)": 1e15, "Kibioctet (KiB, 2¹⁰ B)": 1024.0, "Mébioctet (MiB, 2²⁰ B)": 1024.0**2, "Gibioctet (GiB, 2³⁰ B)": 1024.0**3, "Tébioctet (TiB, 2⁴⁰ B)": 1024.0**4, "Pébioctet (PiB, 2⁵⁰ B)": 1024.0**5}
    RETURN_TYPES, RETURN_NAMES, FUNCTION = ("FLOAT", "STRING"), ("result", "output_str"), "convert"
    @classmethod
    def INPUT_TYPES(cls): return {"required": {"value": ("FLOAT", {"default": 1.0}), "from_unit": (list(cls.UNITS.keys()),), "to_unit": (list(cls.UNITS.keys()),)}}
    def convert(self, value, from_unit, to_unit):
        result = (value * self.UNITS[from_unit]) / self.UNITS[to_unit]
        return (result, f"{value:g} {from_unit.split(' (')[0]} = {result:g} {to_unit.split(' (')[0]}")

class TimeConverterNode:
    CATEGORY = f"{MAIN_CATEGORY}/Converters"
    UNITS = {"Seconde (s)": 1.0, "Milliseconde (ms)": 0.001, "Microseconde (µs)": 1e-6, "Nanoseconde (ns)": 1e-9, "Picoseconde (ps)": 1e-12, "Temps de Planck (tₚ)": 5.391e-44, "Minute (min)": 60.0, "Heure (h)": 3600.0, "Jour (d)": 86400.0, "Semaine (sem)": 604800.0, "Mois (30.44j)": 2629746.0, "Année (365.24j)": 31556952.0, "Année julienne (365.25j)": 31557600.0, "Décennie": 315569520.0, "Siècle": 3155695200.0}
    RETURN_TYPES, RETURN_NAMES, FUNCTION = ("FLOAT", "STRING"), ("result", "output_str"), "convert"
    @classmethod
    def INPUT_TYPES(cls): return {"required": {"value": ("FLOAT", {"default": 1.0}), "from_unit": (list(cls.UNITS.keys()),), "to_unit": (list(cls.UNITS.keys()),)}}
    def convert(self, value, from_unit, to_unit):
        result = (value * self.UNITS[from_unit]) / self.UNITS[to_unit]
        return (result, f"{value:g} {from_unit.split(' (')[0]} = {result:g} {to_unit.split(' (')[0]}")

# =================================================
# ENREGISTREMENT DE TOUS LES NŒUDS POUR COMFYUI
# =================================================
NODE_CLASS_MAPPINGS = {
    "ScientificCalculatorTri": ScientificCalculatorTriNode,
    "UniversalConverter": UniversalConverterNode,
    "ManualRateConverter": ManualRateConverterNode,
    "LengthConverter": LengthConverterNode,
    "MassConverter": MassConverterNode,
    "VolumeConverter": VolumeConverterNode,
    "DataConverter": DataConverterNode,
    "TimeConverter": TimeConverterNode,
}

NODE_DISPLAY_NAME_MAPPINGS = {
    "ScientificCalculatorTri": "🧮 Calculatrice Scientifique",
    "UniversalConverter": "🔧 Convertisseur Universel",
    "ManualRateConverter": "💱 Convertisseur Manuel (Taux)",
    "LengthConverter": "📏 Convertisseur de Longueur",
    "MassConverter": "⚖️ Convertisseur de Masse",
    "VolumeConverter": "💧 Convertisseur de Volume",
    "DataConverter": "💾 Convertisseur de Données",
    "TimeConverter": "⏱️ Convertisseur de Temps",
}
